package modelo;

import modelo.moviles.balas.Bala;
import modelo.moviles.exceptions.ArsenalSinArmas;
import modelo.moviles.exceptions.MovilNoEstaEnLaLista;
import modelo.moviles.*;
import modelo.moviles.naves.Algo42;
import modelo.moviles.naves.AvionCivil;
import modelo.moviles.naves.Avioneta;
import modelo.moviles.naves.Bombardero;
import modelo.moviles.naves.Caza;
import modelo.moviles.naves.CazaNuevo;
import modelo.moviles.naves.Explorador;
import modelo.moviles.naves.Helicoptero;
import modelo.moviles.naves.Nave;

import java.util.*;

import titiritero.ControladorJuego;
import titiritero.KeyPressedObservador;
import titiritero.ObjetoVivo;
import titiritero.SuperficieDeDibujo;
import vista.VentanaAplicacion;
import vista.VentanaSimulacion;
import vista.VistaEleccionDeNivel;
import controlador.ControladorAlgo42;
import controlador.ControladorVista;



public class Nivel  implements ObjetoVivo {


	private static final int  ALTO= 480;
	private static final int  ANCHO= 640;
	private LinkedList<Movil> moviles;
	private int puntaje ;
	private int puntajeNecesario;
	private int cantFilas;
	private int cantColumnas;
	private boolean ganado;
	private boolean perdido;
	private LinkedList<Flota> flotas;
	private Algo42 algo42;
	private ControladorJuego controlador = null;
	private ControladorVista controladorVista;
	private boolean yaCreoHelicoptero ;
	private boolean yaCreoAvionCivil ;
	
	
	public Nivel(int filas, int columnas){
		this.configurarNivel(filas,columnas);
	}

	private void configurarNivel(int filas, int columnas){
		
		this.setCantFilas(filas);
		this.setCantColumnas(columnas);
		moviles = new LinkedList<Movil>() ;
		puntaje = 0;
		puntajeNecesario = 1000;
		ganado = false;
		perdido = false;
		flotas =  new LinkedList<Flota>() ;
		algo42 = new Algo42(this);
		yaCreoHelicoptero= false;
		yaCreoAvionCivil= false;
	}

	/* "sumamos el puntaje obtenido" */
	public void sumarPuntaje(int incremento) {
		
		puntaje =  puntaje + incremento;

		if (this.consiguioPuntajeNecesario() ) {
			this.ganarNivel();
		}
		
	}

	/* "Controla si el puntaje ya es el necesario para pasar el nivel" */
	public boolean consiguioPuntajeNecesario(){
	
		return (puntaje == puntajeNecesario);
	}
	
	
	public void setMoviles(LinkedList<Movil> moviles) {
		this.moviles = moviles;
	}

		public void setPuntaje(int puntaje) {
		this.puntaje = puntaje;
	}

	public int getPuntajeNecesario() {
		return puntajeNecesario;
	}

	public void setPuntajeNecesario(int puntajeNecesario) {
		this.puntajeNecesario = puntajeNecesario;
	}


	public void setCantFilas(int cantFilas) {
		this.cantFilas = cantFilas;
	}

	
	public void setCantColumnas(int cantColumnas) {
		this.cantColumnas = cantColumnas;
	}

	public boolean isGanado() {
		return ganado;
	}

	public void setGanado(boolean ganado) {
		this.ganado = ganado;
	}

	public boolean isPerdido() {
		return perdido;
	}

	public void setPerdido(boolean perdido) {
		this.perdido = perdido;
	}

	public void setFlotas(LinkedList<Flota> flotas) {
		this.flotas = flotas;
	}

	
	public void setAlgo42(Algo42 algo42) {
		this.algo42 = algo42;
	}
	

	/* "Actualizo la lista de flotas: si una flota debe huir les cambia el estado y la saca de la lista" */
	public void actualizarFlotas(){
	
		Iterator<Flota> it = flotas.iterator();
		Flota actual;
		LinkedList<Flota> aBorrar = new LinkedList<Flota>();
		while (it.hasNext()) {
			actual = it.next();
			if (actual.debeHuir() ){					
				actual.huir();
				aBorrar.add(actual);
			}
		}
		it = aBorrar.iterator();
		while (it.hasNext()){
			flotas.remove(it.next());
		}
	}

	/* 	"Agrega una flota a la lista de flotas" */ 
	public void agregarFlota (Flota flotaNueva){
		flotas.add(flotaNueva);
	}

	/*  "Agregamos un movil al terreno" */ 
	public void agregarMovil (Movil movil){
		moviles.add(movil);
	}

	/* "Crea una flota en la lista de flotas" */
	public void crearFlota(){

		Flota flota;
	
		
		flota = new Flota();
		int cantidadAcrear = 0;
	
		
		while (cantidadAcrear <= 15) {
			cantidadAcrear = ((int)  Math.floor(Math.random()  * 20));
		}
	
		int naveGuia = ((int)  Math.floor(Math.random() * cantidadAcrear)); 
		int tipoaCrear ;
		Nave nave;
		for (int i = 1; i <= cantidadAcrear; i++){
	
			tipoaCrear = ((int)  Math.floor(Math.random() * 7)); 
			/* "En base al numero que salga crearemos un tipo de Nave Enemiga"
			"1: Avioneta - 2: Bombardero - 3: Caza - 4: Explorador - 5: AvionCivil - 6: Helicoptero" */
			
			
			switch(tipoaCrear) {
			 case 1: 
				 nave = new Avioneta(this);
				 break;
			 case 2: 
				 nave = new Bombardero(this);
				 break;
			 case 3: 
				 nave = new Caza(this);
				 break;
			 case 4: 
				 nave = new Explorador(this);
				 break;
			 case 5:
				 nave = new CazaNuevo(this);
				 break;
				 
			// Ponemos un control para no crear tantos aviones civiles ni helicopteros	 
			 case 6:
				 if (!yaCreoHelicoptero){
					 nave = new Helicoptero(this);
				 }else{
					 nave = new Explorador(this);
				 }
				 yaCreoHelicoptero = (!yaCreoHelicoptero);
				 break;
			 default:
				 if (!yaCreoAvionCivil){
					 nave = new AvionCivil(this);
				 }else{
					 nave = new Avioneta(this);;
				 }
				 yaCreoAvionCivil = (!yaCreoAvionCivil);
				 break;
			 }
			
			/* "Todas las naves menos los Exploradores deben salir desde arriba" */
			int columna = ((int)  Math.floor(Math.random() * this.getCantColumnas()));
			int fila = 1;
			nave.setPosicion(new Posicion (fila,columna ));	
	
			/* "Los Exploradores y Helicopteros deben salir de la derecha". */
			if ((tipoaCrear == 4) || (tipoaCrear == 6)){
				nave.setPosicion(new Posicion (((int)  Math.floor(Math.random() * this.getCantFilas())), 1));	
			}
			
			if (naveGuia == i ){
				nave.setGuia(true);		
			}
			
			flota.agregarNave(nave) ;
			
			/* Si se creo un caza, creamos 5 en forma de V */
			if ((tipoaCrear == 3) || (tipoaCrear == 5)){
				for (int j = 1; j < 5; j++){
					if (tipoaCrear == 3){
						nave = new Caza(this);
					}else{
						nave = new CazaNuevo(this);
					}
					/* los dos primeros deben estar una posicion mas baja
					 * , despues vuelve a subir*/
					if (j < 3){
						fila = fila - nave.getFactorMovimiento() * 10 ;
					} else {
						fila = fila + nave.getFactorMovimiento() * 10;
					}
					columna = columna + nave.getFactorMovimiento() * 10;

					nave.setPosicion(new Posicion (fila,columna ));	
					flota.agregarNave(nave) ;
				}
			}
		}
		flotas.add(flota );	
	}
	
	/* "devuelve el flag ganado" */
	public boolean fueGanado(){
		return this.ganado;
	}

	/* "devuelve el flag perdido" */
	public boolean fuePerdido() {
		return this.perdido;
	}
	
	/* "Cambia el estado del nivel" */ 
	public void ganarNivel(){
		this.ganado = true;
	}

	/* "Devuelve el algo42 " */
	public Algo42 getAlgo42(){
		return this.algo42;
	}

	/* "Devuelve la cantidad de columnas" */
	public int getCantColumnas(){
		return this.cantColumnas;
	}
	

	/* "Devuelve la cantidad de filas" */ 
	public int getCantFilas() {
		return this.cantFilas;
	}
	
	/* "Devuelve las flotas" */
	public LinkedList<Flota> getFlotas() {
		return flotas;
	}
	
	/* "devuelve el la lista de Moviles" */
	public LinkedList<Movil> getMoviles() {
		return moviles;
	}

	/* "devuelve el Puntaje" */
	public int getPuntaje() {
		return puntaje;
	}


	/* "Cambia el estado del nivel" */
	public void perderNivel(){
		perdido = true;
		VentanaAplicacion.obtenerVentana().setContentPane(new VistaEleccionDeNivel());
	}

	/* "Maneja el comportamiento de un casillero en el que hay mas de un objeto movil." */
	public void procesarCasilleroCompartido(Stack<Movil> pilaMoviles){

		Movil actual , siguiente;

		while (!pilaMoviles.isEmpty()) {
			 actual = pilaMoviles.pop();
			 Iterator<Movil> it = pilaMoviles.iterator();
			 
			 while (it.hasNext()){
				 siguiente = it.next();
				 if (actual.noEstallo() && siguiente.noEstallo()) {
					 actual.encuentroCon(siguiente);
				 }
			 }
		}
	}
	

	/* "Procesa cada casillero del nivel en busca de posiciones ocupadas por mas de un movil. 
	 * Si encuentra ese caso decide como seguir." */
	public void procesarEncuentros(){
	
		Stack<Movil> pilaMoviles = new Stack<Movil>();
		for ( Movil movilAagregar: this.moviles){
			pilaMoviles.push(movilAagregar);
		}
			
		Stack<Movil> movilesCompartiendoCasillero;
		LinkedList<Posicion> posicionesRevisadas = new LinkedList<Posicion>();
		boolean yaSeRevisoLaPosicion;
		Movil actual;
		while (!pilaMoviles.isEmpty()) {
			 actual = pilaMoviles.pop();
		
				movilesCompartiendoCasillero =  new Stack<Movil>();
				yaSeRevisoLaPosicion = false;
				for (Posicion pos: posicionesRevisadas){
					if (pos.mismaPosicion(actual.getPosicion(), actual.getFactorMovimiento()* 2)){
						yaSeRevisoLaPosicion = true;
					}
				}
				if (!yaSeRevisoLaPosicion){
					posicionesRevisadas.add(actual.getPosicion());
					movilesCompartiendoCasillero.push(actual);
					Iterator<Movil> it = pilaMoviles.iterator();
					 Movil siguiente;
					 while (it.hasNext()){
						 siguiente = it.next();
						 if (actual.getPosicion().mismaPosicion(siguiente.getPosicion(), actual.getFactorMovimiento())) {
							 movilesCompartiendoCasillero.push(siguiente);
						 }
					 }
					 if (movilesCompartiendoCasillero.size() > 1) {
						 this.procesarCasilleroCompartido(movilesCompartiendoCasillero);
					 }
				}
		}
	}
	
	

/* "Remuevo una flota de la lista de flotas" */
 public void removerFlota(Flota flotaHuyendo){
	 flotas.remove(flotaHuyendo);
 }



	/* "Sacamos un movil del terreno"*/ 
 public void removerMovil(Movil movil)throws MovilNoEstaEnLaLista {
	 if(!moviles.remove(movil)) {
		 throw new MovilNoEstaEnLaLista();
	 }

 }


 /* "Movemos todos los objetos moviles" */
 public void seguirViviendo(){
  this.procesarEncuentros();
	this.actualizarFlotas();
	 
	 for (int i=0 ; i< moviles.size() ; i++){
			Movil movil = moviles.get(i);
			if (movil.noEstallo()){
				movil.vivir();
			}
	}
	 
	 
	 Iterator<Movil> it = moviles.iterator();
	 Movil actual;
	 while (it.hasNext()){
		 actual = it.next();
		 if (!actual.noEstallo()){
			 it.remove();
		 }
	 }   

		/* "Pongo una condicion para crear una nueva flota en el nivel" */
		int numeroDivisiblepor100 = ((int)  Math.floor(Math.random() * 1000));
		if ((numeroDivisiblepor100 % 100) == 0) {
			this.crearFlota();
		}

		
	 
 }

	
	
 
 public Nivel(SuperficieDeDibujo superficieDeDibujo){
		
		this.configurarNivel(ALTO, ANCHO);
		this.crearFlota();
					
		/*
		* Luego instancio los objetos del framework: una ventana y el controlador
		*/		
		controlador = new ControladorJuego(true);
		
		
		VentanaSimulacion ventana= new VentanaSimulacion(controlador); 
		this.controlador.setSuperficieDeDibujo(ventana.getSuperficieDeDibujo());

	
		controlador.agregarObjetoVivo(this);
						   		
		KeyPressedObservador contrlAlgo42 = new ControladorAlgo42 (this);
		controlador.agregarKeyPressObservador(contrlAlgo42);
		
		
		controladorVista= new ControladorVista(controlador);
		controladorVista.setNivel(this);
		
		
		/*
		 * finalmente establezco el intervalo de sleep dentro del gameloop
		 * y comienzo a ejecutar
		 */
		
		controlador.setIntervaloSimulacion(10);
			
			
		controladorVista= new ControladorVista(controlador);
		controladorVista.setNivel(this);
	}
			
	public void comenzar(){
		controlador.comenzarJuegoAsyn();	
	}
			
	public void detener() {
		controlador.detenerJuego();
	}
			
	public void vivir() {
		controladorVista.generadorVista();
		this.seguirViviendo();	
			
	}

	public void terminarNivel() {
		for (Movil actual: moviles){
			actual.estallar();
		}
		
	}
}

